/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.medbeans.modules.search.types;


import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.actions.NodeAction;
import org.openide.util.actions.SystemAction;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.windows.OutputEvent;
import org.openide.windows.OutputListener;


/**
 * Test DataObject primaryFile for line full-text match.
 *
 * @author  Petr Kuzel
 */
public class FullTextType extends TextType {

    /** Serial version UID. */
    public static final long serialVersionUID = 1L;

    /** Holds details for data objects which satisfied the criteria. 
     * Maps data object to list of details. */
    private Map detailsMap;
    

    /** Clones this object. Overrides */
    public Object clone() {
        FullTextType fullTextType = (FullTextType)super.clone();
        
        // Deserialized of previous version.
        if(fullTextType.detailsMap == null) {
            fullTextType.detailsMap = new HashMap(20);
        }
        
        fullTextType.detailsMap.clear();
        
        return fullTextType;
    }

    /** Gets details map. */
    private Map getDetailsMap() {
        if(detailsMap != null) {
            return detailsMap;
        }
        
        synchronized(this) {
            if(detailsMap == null) {
                detailsMap = new HashMap(20);
            }
        }
        
        return detailsMap;
    }
    
    /** Processes acuall search on data object. */
    public boolean testDataObject(DataObject dobj) {
        try {
            String line = ""; //NOI18N
            boolean hit = false;

            // primary file of the DataObject
            FileObject fo = dobj.getPrimaryFile();
            if (fo == null) 
                return false;

            // primary file content
            InputStream is = fo.getInputStream();
            LineNumberReader reader = new LineNumberReader(new InputStreamReader(is));

            TextDetail detail = null;

            List txtDetails = new ArrayList(5);
            
            while (true) {
                line = reader.readLine();
                if (line == null) break;

                if (matchString != null) {
                    int lineNum = reader.getLineNumber(); // current line + 1
                    int markLen = matchString.length();

                    int i = matchString(line, 0);
                    while (i >= 0) {
                        TextDetail det = new TextDetail(dobj);
                        det.setLine(lineNum);
                        det.setColumn(i+1); // current column + 1
                        det.setLineText(line);
                        
                        det.setMarkLength(markLen);
                        
                        txtDetails.add(det);

                        i = matchString(line, i+1);
                    }
                } else if(matchRE(line)) {
                    TextDetail det = new TextDetail(dobj);
                    det.setLine(reader.getLineNumber());
                    det.setLineText(line);
                    
                    txtDetails.add(det);
                }
            }

            if(txtDetails.isEmpty()) {
                return false;
            }
            
            getDetailsMap().put(dobj, txtDetails);

            return true;
        } catch (FileNotFoundException fnfe) {
            return false;
        } catch (IOException ioe) {
            if (Boolean.getBoolean("netbeans.debug.exceptions")) // NOI18N
                ioe.printStackTrace();
            return false;
        }
    }
    

    /** Gets detail nodes for specified found object instance. */
    public Node[] getDetails(Object resultObject) {
        List details = (List)getDetailsMap().get(resultObject);
        
        if(details == null)
            return null;

        List detailNodes = new ArrayList(details.size());
        
        for(Iterator it = details.iterator(); it.hasNext();) {
            
            TextDetail txtDetail = (TextDetail)it.next();
        
            Node detailNode = new DetailNode(txtDetail);
            detailNodes.add(detailNode);        
        }

        return (Node[])detailNodes.toArray(new Node[detailNodes.size()]);
    }

    /** Gets detail nodes for specified found node. */
    public Node[] getDetails(Node node) {
        DataObject dataObject = (DataObject)node.getCookie(DataObject.class);
        
        if(dataObject == null) 
            return null;
        
        return getDetails(dataObject);
    }

    
    
    /** Node which represents detail of one occurence serached text. */
    private static class DetailNode extends AbstractNode implements OutputListener {
        
        /** Detail to represent. */
        private TextDetail txtDetail;

        
        /** Construct <code>DetailNode</code> representing specified text detail. */
        public DetailNode(TextDetail txtDetail) {
            super(Children.LEAF);
            
            this.txtDetail = txtDetail;
            
            setDefaultAction(SystemAction.get(GotoDetailAction.class));
            setShortDescription(DetailNode.getFullDesc(txtDetail));
        }

        
        /** Creates actions for this node. */
        protected SystemAction[] createActions() {
            return new SystemAction[] {
                SystemAction.get(GotoDetailAction.class),
                SystemAction.get(ShowDetailAction.class)
            };
        }

        /** Gets name for this action. */
        public String getName() {
            return DetailNode.getShortDesc(txtDetail);
        }

        /** Goes to text occurence. */
        private void gotoDetail() {
            txtDetail.showDetail(TextDetail.DH_GOTO);
        }

        /** Show the text occurence. */
        private void showDetail() {
            txtDetail.showDetail(TextDetail.DH_SHOW);
        }

        /** Implements <code>OutputListener</code> interface method. */
        public void outputLineSelected (OutputEvent evt) {
            txtDetail.showDetail(TextDetail.DH_SHOW);
        }

        /** Implements <code>OutputListener</code> interface method. */        
        public void outputLineAction (OutputEvent evt) {
            txtDetail.showDetail(TextDetail.DH_GOTO);
        }

        /** Implements <code>OutputListener</code> interface method. */
        public void outputLineCleared (OutputEvent evt) {
            txtDetail.showDetail(TextDetail.DH_HIDE);
        }

        /** Sets short description fot text detail. */
        private static String getShortDesc(TextDetail det) {
            if(det.getColumn() > 0) {
                Object[] args = new Object[] { Integer.toString(det.getLine()),
                                               Integer.toString(det.getColumn()) };

                return MessageFormat.format(NbBundle.getBundle(FullTextType.class).getString("TEXT_DETAIL_FMT_SHORT1"), args); // NOI18N
            } else {
                Object[] args = new Object[] { Integer.toString(det.getLine()) };

                return MessageFormat.format(NbBundle.getBundle(FullTextType.class).getString("TEXT_DETAIL_FMT_SHORT2"), args); // NOI18N
            }
        }

        /** Sets full description fot text detail. */
        private static String getFullDesc(TextDetail det) {
            FileObject fo = det.getDataObject().getPrimaryFile();
            String filename = fo.getName() + "." + fo.getExt(); // NOI18N
            String line = det.getLineText();

            if (det.getColumn() > 0) {
                Object[] args = new Object[] { line.trim(),
                                               filename,
                                               Integer.toString(det.getLine()),
                                               Integer.toString(det.getColumn()) };

                return MessageFormat.format(NbBundle.getBundle(FullTextType.class).getString("TEXT_DETAIL_FMT_FULL1"), args); // NOI18N
            } else {
                Object[] args = new Object[] { line.trim(),
                                               filename,
                                               Integer.toString(det.getLine()) };

                return MessageFormat.format(NbBundle.getBundle(FullTextType.class).getString("TEXT_DETAIL_FMT_FULL2"), args); // NOI18N
            }
        }
        
    } // End of DetailNode class.

    
    /** Action on detail node which goes to text occurence. */
    private static class GotoDetailAction extends NodeAction {
        
        /** Gets name of the action. Implements superclass abstract method. */
        public String getName() {
            return NbBundle.getBundle(FullTextType.class).getString("LBL_GotoDetailAction");
        }
        
        /** Gets help context fot this action. Implements superclass abstract method. */
        public HelpCtx getHelpCtx() {
            return new HelpCtx(GotoDetailAction.class);
        }

        /** Tests wheter this action should be enables on specified activated nodes. 
         * Implements superclass abstract method. */
        protected boolean enable(Node[] activatedNodes) {
            if(activatedNodes == null || activatedNodes.length == 0)
                return false;
            
            if(activatedNodes[0] instanceof DetailNode)
                return true;
            
            return false;
        }

        /** Performs action on specified activated nodes. Implements superclass
         * abstract method. */
        protected void performAction(Node[] activatedNodes) {
            if(activatedNodes == null || activatedNodes.length == 0)
                return;

            if(activatedNodes[0] instanceof DetailNode)
                ((DetailNode)activatedNodes[0]).gotoDetail();
        }
        
        
    } // End of GotoDetailAction class.

    
    /** Action on detail node which shows the text occurence. */
    private static class ShowDetailAction extends NodeAction {

        /** Gets name of the action. Implements superclass abstract method. */
        public String getName() { 
            return NbBundle.getBundle(FullTextType.class).getString("LBL_ShowDetailAction");
        }

        /** Gets help context fot this action. Implements superclass abstract method. */
        public HelpCtx getHelpCtx() {
            return new HelpCtx(ShowDetailAction.class);
        }

        /** Tests wheter this action should be enables on specified activated nodes. 
         * Implements superclass abstract method. */
        protected boolean enable(Node[] activatedNodes) {
            if(activatedNodes == null || activatedNodes.length == 0)
                return false;
            
            if(activatedNodes[0] instanceof DetailNode)
                return true;
            
            return false;
        }

        /** Performs action on specified activated nodes. Implements superclass
         * abstract method. */
        protected void performAction(Node[] activatedNodes) {
            if(activatedNodes == null || activatedNodes.length == 0)
                return;
            
            if(activatedNodes[0] instanceof DetailNode)
                ((DetailNode)activatedNodes[0]).showDetail();
        }
    } // End of ShowDetailAction class.

    /** Gets help context for this search type.
     * Implements superclass abstract method. */
    public HelpCtx getHelpCtx() {
        return new HelpCtx(FullTextType.class);
    }
        
}
